package sbc.group1.lab1.mozartspaces;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;

import org.mozartspaces.capi3.AnyCoordinator;
import org.mozartspaces.core.Capi;
import org.mozartspaces.core.ContainerReference;
import org.mozartspaces.core.Entry;
import org.mozartspaces.core.MzsConstants;
import org.mozartspaces.core.MzsCore;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.TransactionReference;
import org.mozartspaces.core.MzsConstants.RequestTimeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sbc.group1.lab1.common.Order;

abstract class Space {

    private static final Logger log = LoggerFactory.getLogger(Space.class);
    private static final int CACHE_SIZE = 100;
    
    public static final String ASSEMBLED_CAR_CONTAINER_NAME = "assembledCarContainer";
    public static final String COMPLETED_CAR_CONTAINER_NAME = "completedCarContainer";
    public static final String COMPLETENESS_TESTED_CONTAINER_NAME = "completenessTestedContainer";
    public static final String DEFECTS_TESTED_CONTAINER_NAME = "defectsTestedContainer";
    public static final String TESTED_CAR_CONTAINER_NAME = "testedCarContainer";
    public static final String DEFECT_CAR_CONTAINER_NAME = "defectCarContainer";
    public static final String FINISHED_CAR_CONTAINER_NAME = "finishedCarContainer";
    
    public static final String CAR_BODY_CONTAINER_NAME = "carBodyContainer";
    public static final String ENGINE_CONTAINER_NAME = "engineContainer";
    public static final String WHEEL_CONTAINER_NAME = "wheelContainer";
    
    public static final String ORDER_INDEX_CONTAINER_NAME = "orderIndexContainer";
    public static final String ORDER_CONTAINER_NAME = "orderContainer";
    public static final String COATING_JOB_CONTAINER_NAME = "coatingJobContainer";
    public static final String ASSEMBLY_JOB_CONTAINER_NAME = "assemblyJobContainer";
    public static final String CARS_LEFT_CONTAINER_NAME = "carsLeftContainer";
    public static final String ORDER_JOB_REISSUE_CONTAINER_NAME = "orderJobReissueContainer";
    
    protected Capi capi;
    protected MzsCore core;

    //Not coated
    protected ContainerReference assembledCarContainer;
    //Coated
    protected ContainerReference completedCarContainer;
    protected ContainerReference defectsTestedContainer;
    protected ContainerReference completenessTestedContainer;
    //Tested - FIFO
    protected ContainerReference testedCarContainer;
    //With defect parts
    protected ContainerReference defectCarContainer;
    //Marked as finished
    protected ContainerReference finishedCarContainer;
    
    protected ContainerReference carBodyContainer;
    protected ContainerReference engineContainer;
    protected ContainerReference wheelContainer;
    
    protected ContainerReference orderContainer;
    //container to get an id for orders
    protected ContainerReference orderIndexContainer;
    
    protected ContainerReference carCoatingJobContainer;
    
    protected ContainerReference orderJobsReissueContainer;
    
    private Cache<Integer, ContainerReference> assemblyJobContainers =
        new Cache<Integer, ContainerReference>(CACHE_SIZE);
    private Cache<Integer, ContainerReference> coatedCarBodyContainers =
        new Cache<Integer, ContainerReference>(CACHE_SIZE);
    private Cache<Integer, ContainerReference> coatingJobContainers =
        new Cache<Integer, ContainerReference>(CACHE_SIZE);
    private Cache<Integer, ContainerReference> carsLeftContainers =
        new Cache<Integer, ContainerReference>(CACHE_SIZE);;
    
    public Space() {
        new HashMap<Integer, ContainerReference>();
    }
    
    public Capi getCapi() {
        return capi;
    }
    
    public MzsCore getCore() {
        return core;
    }
    
    
    public ContainerReference getAssembledCarContainer() {
        return assembledCarContainer;
    }
    
    public ContainerReference getCompletedCarContainer() {
        return completedCarContainer;
    }
    
    public ContainerReference getTestedCarContainer() {
        return testedCarContainer;
    }
    
    public ContainerReference getDefectCarContainer() {
        return defectCarContainer;
    }
    
    public ContainerReference getFinishedCarContainer() {
        return finishedCarContainer;
    }
    
    public ContainerReference getCarBodyContainer() {
        return carBodyContainer;
    }
    
    public ContainerReference getEngineContainer() {
        return engineContainer;
    }
    
    public ContainerReference getWheelContainer() {
        return wheelContainer;
    }
    
    public ContainerReference getCompletenessTestedContainer() {
        return completenessTestedContainer;
    }

    public ContainerReference getDefectsTestedContainer() {
        return defectsTestedContainer;
    }
    
    public ContainerReference getOrderContainer() {
        return orderContainer;
    }
    
    public ContainerReference getOrderIndexContainer() {
        return orderIndexContainer;
    }
    
    
    public ContainerReference getOrderJobsReissueContainer() {
        return orderJobsReissueContainer;
    }
    
    public ContainerReference getAssemblyJobContainer(Order order) throws MzsCoreException {
       return getCachedContainer(assemblyJobContainers, 
               ASSEMBLY_JOB_CONTAINER_NAME, order);
    }
    
    public ContainerReference getCoatingJobContainer(Order order) throws MzsCoreException {
        return getCachedContainer(coatingJobContainers, 
                COATING_JOB_CONTAINER_NAME, order);
    }
    
    public ContainerReference getCarsLeftContainer(Order order) throws MzsCoreException {
        return getCachedContainer(carsLeftContainers,
               CARS_LEFT_CONTAINER_NAME, order);
    }
    
    public ContainerReference getCoatedCarBodyContainer(Order order) throws MzsCoreException {
        return getCachedContainer(coatedCarBodyContainers,
               CAR_BODY_CONTAINER_NAME, order);
    }
    
    private ContainerReference getCachedContainer(Cache<Integer, ContainerReference> cache, 
            String containerBaseName, Order order) throws MzsCoreException {
        ContainerReference ret = cache.get(order.getId());
        if (ret==null) {
            ret =  capi.lookupContainer(containerBaseName+order.getId(), 
                    getSpaceUri(),RequestTimeout.DEFAULT,null);
            cache.put(order.getId(), ret);
        }
        return ret;
    }
    
    
    
    public abstract URI getSpaceUri();
    
    public void addOrder(Order order) throws MzsCoreException {
        TransactionReference transaction = capi.createTransaction(
                RequestTimeout.INFINITE, getSpaceUri());
        try {
            capi.write(orderIndexContainer, new Entry(order));
            order = capi.<Order>take(orderIndexContainer, AnyCoordinator.newSelector(), 
                    MzsConstants.RequestTimeout.INFINITE, transaction).get(0);
            capi.write(orderContainer, RequestTimeout.INFINITE,transaction, new Entry(order));
            capi.createContainer(CAR_BODY_CONTAINER_NAME+order.getId(),
                    getSpaceUri(),MzsConstants.Container.UNBOUNDED, transaction);
            ContainerReference assemblyJobContainer = 
                capi.createContainer(ASSEMBLY_JOB_CONTAINER_NAME+order.getId(),
                    getSpaceUri(),MzsConstants.Container.UNBOUNDED, transaction);
            ContainerReference paintJobContainer = 
                capi.createContainer(COATING_JOB_CONTAINER_NAME+order.getId(),
                    getSpaceUri(),MzsConstants.Container.UNBOUNDED, transaction);
            ContainerReference carsLeftContainer = 
                capi.createContainer(CARS_LEFT_CONTAINER_NAME+order.getId(),
                    getSpaceUri(),MzsConstants.Container.UNBOUNDED, transaction);
            
            ArrayList<Entry> assemblyJobs = new ArrayList<Entry>(order.getNumCars());
            ArrayList<Entry> paintJobs = new ArrayList<Entry>(order.getNumCars());
            ArrayList<Entry> leftCars = new ArrayList<Entry>(order.getNumCars());
            for (int i = 0; i<order.getNumCars(); i++) {
                assemblyJobs.add(new Entry(new AssemblyJob(order.getColor(), order.getEngine())));
                paintJobs.add(new Entry(new CoatingJob(order)));
                leftCars.add(new Entry(new Token()));
            }
            leftCars.remove(leftCars.size()-1);
            
            capi.write(assemblyJobs, assemblyJobContainer, MzsConstants.RequestTimeout.INFINITE,transaction);
            capi.write(paintJobs, paintJobContainer, MzsConstants.RequestTimeout.INFINITE,transaction);
            capi.write(leftCars, carsLeftContainer, MzsConstants.RequestTimeout.INFINITE,transaction);
            capi.commitTransaction(transaction);
        }catch (MzsCoreException e) {
            capi.rollbackTransaction(transaction);
            throw e;
        }
    }
    
    public void finishOrder(Order order, TransactionReference transaction) throws MzsCoreException {
            capi.delete(orderContainer,OrderCoordinator.newSelector(1,
                    order.getId()), 
                    MzsConstants.TransactionTimeout.INFINITE,transaction);
            capi.destroyContainer(getCoatingJobContainer(order), transaction);
            capi.destroyContainer(getAssemblyJobContainer(order), transaction);
            capi.destroyContainer(getCarsLeftContainer(order), transaction);
            capi.destroyContainer(getCoatedCarBodyContainer(order), transaction);
            log.info("Finished Order #"+order.getId());
    }

}
